package com.admin_system.controller;

import com.admin_system.pojo.Material;
import com.admin_system.service.MaterialService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.CacheControl;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/**
 * 通用物料控制器
 * 管理系统通用物料的上传、下载和管理
 * 
 * 提供以下主要功能：
 * 1. 物料文件的上传、下载与删除
 * 2. 物料列表的获取与分页查询
 * 3. 多种格式文件的在线预览支持
 * 4. 基于角色的访问控制
 */
@RestController
@RequestMapping("/api/materials")
public class MaterialController {

    private static final Logger logger = LoggerFactory.getLogger(MaterialController.class);

    @Autowired
    private MaterialService materialService;

    /**
     * 获取所有物料列表
     * 返回系统中所有物料的详细信息，包括ID、名称、描述、文件类型等
     * 
     * @return 物料列表JSON数组
     */
    @GetMapping
    public ResponseEntity<List<Material>> getAllMaterials() {
        logger.info("获取所有物料列表");
        return ResponseEntity.ok(materialService.getAllMaterials());
    }

    /**
     * 上传物料文件
     * 将文件保存到系统中并创建对应的物料记录
     * 只有管理员才能上传文件（基于角色控制）
     * 
     * @param file 上传的物料文件（MultipartFile格式）
     * @param description 物料描述信息
     * @return 上传结果，包含成功状态和新创建的物料信息
     */
    @PostMapping("/upload")
    @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<?> uploadMaterial(
            @RequestParam("file") MultipartFile file,
            @RequestParam("description") String description) {
        
        logger.info("上传物料文件: {}, 描述: {}", file.getOriginalFilename(), description);
        
        try {
            Material material = materialService.saveMaterial(file, description);
            return ResponseEntity.ok(Map.of(
                "success", true,
                "message", "文件上传成功",
                "material", material
            ));
        } catch (Exception e) {
            logger.error("文件上传失败", e);
            return ResponseEntity.badRequest().body(Map.of(
                "success", false,
                "message", "文件上传失败: " + e.getMessage()
            ));
        }
    }

    /**
     * 下载物料文件
     * 根据物料ID获取对应的文件资源并下载
     * 使用流式传输减少内存使用
     * 
     * @param id 物料ID
     * @return 文件资源流，以附件形式下载
     */
    @GetMapping("/download/{id}")
    public ResponseEntity<Resource> downloadMaterial(@PathVariable Long id) {
        logger.info("下载物料文件, ID: {}", id);
        
        try {
            Map<String, Object> result = materialService.getMaterialFile(id);
            Resource fileResource = (Resource) result.get("resource");
            String filename = (String) result.get("filename");
            
            // 设置响应头，指定文件名和内容类型
            String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString())
                    .replaceAll("\\+", "%20");
            
            // 为大文件添加Content-Length头，帮助前端显示下载进度
            long contentLength = fileResource.contentLength();
            
            // 设置缓存控制头，减少重复请求
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedFilename);
            headers.setContentLength(contentLength);
            headers.setCacheControl("no-cache");
            headers.setPragma("no-cache");
            headers.setExpires(0);
            
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(fileResource);
        } catch (Exception e) {
            logger.error("文件下载失败", e);
            return ResponseEntity.notFound().build();
        }
    }
    
    /**
     * 预览物料文件
     * 根据文件类型提供不同的预览方式，支持多种格式：
     * - PDF：直接浏览器内预览
     * - 图片：直接显示
     * - 视频/音频：使用浏览器内媒体播放器
     * - Office文档：使用特殊预览服务
     * - 文本文件：直接显示内容
     * 
     * @param id 物料ID
     * @param request HTTP请求对象（用于构建服务器URL）
     * @return 根据文件类型返回不同的预览响应
     */
    @GetMapping("/preview/{id}")
    public ResponseEntity<?> previewMaterial(@PathVariable Long id, HttpServletRequest request) {
        logger.info("预览物料文件, ID: {}", id);
        
        try {
            Material material = materialService.getMaterialById(id);
            if (material == null) {
                return ResponseEntity.notFound().build();
            }
            
            // 提取文件信息
            String filename = material.getFileName();
            String fileType = material.getFileType();
            String fileExtension = "";
            if (filename != null && filename.contains(".")) {
                fileExtension = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
            }
            
            // 准备文件名编码
            String encodedFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString())
                    .replaceAll("\\+", "%20");
            
            // 创建响应对象
            Map<String, Object> response = new HashMap<>();
            
            // 检查文件类型是否支持预览
            boolean canPreview = false;
            
            // 判断文件大小，超过阈值的文件使用下载方式处理
            long fileSizeMB = material.getFileSize() / (1024 * 1024);
            if (fileSizeMB > 50) { // 超过50MB的文件提示下载
                // 大文件不适合在线预览
                response.put("previewType", "large_file");
                response.put("fileName", filename);
                response.put("fileSize", fileSizeMB + "MB");
                response.put("message", "文件较大（" + fileSizeMB + "MB），建议下载后查看");
                response.put("downloadUrl", "/api/materials/download/" + id);
                response.put("contentType", fileType);
                return ResponseEntity.ok(response);
            }
            
            // 根据文件类型决定如何预览
            if (fileType != null) {
                // PDF文件可以直接在浏览器中预览
                if (fileType.equals("application/pdf") || fileExtension.equals("pdf")) {
                    // 获取文件资源并直接返回用于内联显示
                    Map<String, Object> result = materialService.getMaterialFile(id);
                    Resource fileResource = (Resource) result.get("resource");
                    
                    // 设置缓存控制头，启用缓存以提高性能
                    HttpHeaders headers = new HttpHeaders();
                    headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename*=UTF-8''" + encodedFilename);
                    headers.setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS));
                    headers.setExpires(System.currentTimeMillis() + 3600000);
                    
                    return ResponseEntity.ok()
                            .headers(headers)
                            .contentType(MediaType.APPLICATION_PDF)
                            .contentLength(fileResource.contentLength())
                            .body(fileResource);
                }
                // 图片文件可以直接在浏览器中预览
                else if (fileType.startsWith("image/")) {
                    // 获取文件资源并直接返回用于内联显示
                    Map<String, Object> result = materialService.getMaterialFile(id);
                    Resource fileResource = (Resource) result.get("resource");
                    
                    // 设置缓存控制头，启用缓存以提高性能
                    HttpHeaders headers = new HttpHeaders();
                    headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename*=UTF-8''" + encodedFilename);
                    headers.setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS));
                    headers.setExpires(System.currentTimeMillis() + 3600000);
                    
                    return ResponseEntity.ok()
                            .headers(headers)
                            .contentType(MediaType.parseMediaType(fileType))
                            .contentLength(fileResource.contentLength())
                            .body(fileResource);
                }
                // 视频和音频文件可以使用HTML5播放器预览
                else if (fileType.startsWith("video/") || fileType.startsWith("audio/")) {
                    // 返回JSON响应，包含用于创建HTML5播放器的信息
                    response.put("previewType", fileType.startsWith("video/") ? "video" : "audio");
                    response.put("fileName", filename);
                    response.put("contentType", fileType);
                    
                    // 获取当前服务器地址，用于构建完整URL
                    String serverUrl = getServerUrl(request);
                    // 构建一个可访问的文件URL，确保加上上下文路径
                    String publicAccessPath = "/api/materials/public/" + id + "/" + encodedFilename;
                    String fileUrl = serverUrl + publicAccessPath;
                    
                    // 将fileExtension添加到响应中，帮助前端确定正确的MIME类型
                    response.put("fileExtension", fileExtension);
                    response.put("fileUrl", fileUrl);
                    canPreview = true;
                }
                // Office文档使用公共URL和Office Online Viewer预览
                else if (fileType.equals("application/vnd.openxmlformats-officedocument.wordprocessingml.document") ||
                         fileType.equals("application/msword") ||
                         fileType.equals("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") ||
                         fileType.equals("application/vnd.ms-excel") ||
                         fileType.equals("application/vnd.openxmlformats-officedocument.presentationml.presentation") ||
                         fileType.equals("application/vnd.ms-powerpoint")) {
                    
                    // 获取当前服务器地址，用于构建完整URL
                    String serverUrl = getServerUrl(request);
                    // 构建一个可访问的文件URL，不需要身份验证
                    String publicAccessPath = "/api/materials/public/" + id + "/" + encodedFilename;
                    String fileUrl = serverUrl + publicAccessPath;
                    
                    response.put("previewType", "office");
                    response.put("fileName", filename);
                    response.put("fileUrl", fileUrl);
                    response.put("contentType", fileType);
                    canPreview = true;
                }
                // 文本文件可以直接在浏览器中预览
                else if (fileType.startsWith("text/") || 
                         fileType.equals("application/json") ||
                         fileType.equals("application/javascript") ||
                         fileType.equals("application/xml")) {
                    // 获取文件资源
                    Map<String, Object> result = materialService.getMaterialFile(id);
                    Resource fileResource = (Resource) result.get("resource");
                    
                    // 设置缓存控制头，启用缓存以提高性能
                    HttpHeaders headers = new HttpHeaders();
                    headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename*=UTF-8''" + encodedFilename);
                    headers.setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS));
                    headers.setExpires(System.currentTimeMillis() + 3600000);
                    headers.setContentLength(fileResource.contentLength());
                    
                    return ResponseEntity.ok()
                            .headers(headers)
                            .contentType(MediaType.parseMediaType(fileType))
                            .body(fileResource);
                }
                // 不支持预览的文件类型，提供下载信息
                else {
                    response.put("previewType", "not_supported");
                    response.put("fileName", filename);
                    response.put("message", "此类型文件不支持在线预览，请下载后查看");
                    response.put("downloadUrl", "/api/materials/download/" + id);
                    response.put("contentType", fileType);
                    canPreview = false;
                }
            }
            
            // 如果是不支持预览的文件类型或者未知类型
            if (!canPreview) {
                // 始终返回JSON响应，统一由前端处理下载逻辑
                if (response.isEmpty()) {
                    response.put("previewType", "not_supported");
                    response.put("fileName", filename);
                    response.put("message", "此类型文件不支持在线预览，请下载后查看");
                    response.put("downloadUrl", "/api/materials/download/" + id);
                    response.put("contentType", fileType);
                }
                
                return ResponseEntity.ok(response);
            }
            
            // 返回可预览文件的处理信息
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            logger.error("文件预览失败", e);
            return ResponseEntity.badRequest().body(Map.of(
                "success", false,
                "message", "文件预览失败: " + e.getMessage()
            ));
        }
    }

    /**
     * 删除物料
     * 只有管理员才能删除文件
     * @param id 物料ID
     * @return 删除结果
     */
    @DeleteMapping("/{id}")
    @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<?> deleteMaterial(@PathVariable Long id) {
        logger.info("删除物料, ID: {}", id);
        
        try {
            materialService.deleteMaterial(id);
            return ResponseEntity.ok(Map.of(
                "success", true,
                "message", "文件删除成功"
            ));
        } catch (Exception e) {
            logger.error("文件删除失败", e);
            return ResponseEntity.badRequest().body(Map.of(
                "success", false,
                "message", "文件删除失败: " + e.getMessage()
            ));
        }
    }

    /**
     * 构建服务器URL
     * @param request HTTP请求
     * @return 服务器URL，包含协议、主机和端口
     */
    private String getServerUrl(HttpServletRequest request) {
        String scheme = request.getScheme();
        String serverName = request.getServerName();
        int serverPort = request.getServerPort();
        
        StringBuilder url = new StringBuilder();
        url.append(scheme).append("://").append(serverName);
        
        // 只有当端口不是默认的80（HTTP）或443（HTTPS）时才添加
        if ((serverPort != 80) && (serverPort != 443)) {
            url.append(":").append(serverPort);
        }
        
        return url.toString();
    }

    /**
     * 获取公开访问的物料文件（用于文档预览）
     * 这个接口不需要身份验证，但是有ID和文件名参数限制
     * 添加缓存控制以提高性能
     * 
     * @param id 物料ID
     * @param filename 文件名（用于校验）
     * @return 文件资源
     */
    @GetMapping("/public/{id}/{filename}")
    public ResponseEntity<Resource> getPublicMaterial(
            @PathVariable Long id, 
            @PathVariable String filename) {
        logger.info("获取公开物料文件, ID: {}, 文件名: {}", id, filename);
        
        try {
            // 获取物料信息
            Material material = materialService.getMaterialById(id);
            if (material == null) {
                return ResponseEntity.notFound().build();
            }
            
            // 简单验证：检查所请求的文件名是否匹配
            String encodedOriginalFilename;
            try {
                encodedOriginalFilename = URLEncoder.encode(material.getFileName(), StandardCharsets.UTF_8.toString())
                        .replaceAll("\\+", "%20");
            } catch (Exception e) {
                logger.error("文件名编码失败", e);
                return ResponseEntity.badRequest().build();
            }
            
            // 修改匹配逻辑：解码传入的文件名和存储的原始文件名进行比较
            String decodedRequestFilename;
            try {
                decodedRequestFilename = java.net.URLDecoder.decode(filename, StandardCharsets.UTF_8.toString());
            } catch (Exception e) {
                // 如果解码失败，使用原始文件名
                decodedRequestFilename = filename;
            }
            
            // 比较解码后的文件名与原始文件名
            if (!material.getFileName().equals(decodedRequestFilename)) {
                logger.warn("文件名不匹配: 请求={}, 实际={}", decodedRequestFilename, material.getFileName());
                return ResponseEntity.badRequest().build();
            }
            
            // 获取文件资源
            Map<String, Object> result = materialService.getMaterialFile(id);
            Resource fileResource = (Resource) result.get("resource");
            
            // 返回文件，设置为内联显示
            String contentType = material.getFileType();
            // 确保ContentType不为null，默认使用通用二进制类型
            MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
            if (contentType != null && !contentType.isEmpty()) {
                try {
                    mediaType = MediaType.parseMediaType(contentType);
                } catch (Exception e) {
                    logger.warn("无法解析Content-Type: {}, 使用默认类型", contentType);
                }
            }
            
            // 设置缓存控制头，启用缓存以提高性能
            HttpHeaders headers = new HttpHeaders();
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "inline; filename*=UTF-8''" + encodedOriginalFilename);
            headers.setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS));
            headers.setExpires(System.currentTimeMillis() + 3600000);
            headers.setContentLength(fileResource.contentLength());
            
            return ResponseEntity.ok()
                    .headers(headers)
                    .contentType(mediaType)
                    .body(fileResource);
        } catch (Exception e) {
            logger.error("获取公开物料文件失败", e);
            return ResponseEntity.badRequest().build();
        }
    }

    /**
     * 清理孤立的物料文件
     * 删除物理存在但数据库中没有记录的文件
     * 仅管理员可以执行此操作
     * 
     * @return 清理结果，包含成功和失败的数量
     */
    @PostMapping("/cleanup")
    @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<?> cleanupOrphanedFiles() {
        logger.info("执行孤立物料文件清理");
        
        try {
            Map<String, Integer> result = materialService.cleanupOrphanedFiles();
            
            // 构建更友好的响应
            int successCount = result.getOrDefault("success", 0);
            int failedCount = result.getOrDefault("failed", 0);
            
            Map<String, Object> response = new HashMap<>();
            response.put("success", true);
            response.put("message", String.format("清理完成。成功删除 %d 个文件，失败 %d 个", successCount, failedCount));
            response.put("details", result);
            
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            logger.error("清理孤立文件失败", e);
            return ResponseEntity.badRequest().body(Map.of(
                "success", false,
                "message", "清理孤立文件失败: " + e.getMessage()
            ));
        }
    }
} 